import socket
from threading import Thread, Event

from util.log import getLogger
logger = getLogger(10)

from common import *
from protocal import PACK_HEAD_ERROR, PACK_HEAD_HEART

class UdpClient(Thread):
    def __init__(self, ipaddr):
        super().__init__()

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.connect(ipaddr)
        self.sock.settimeout(TIMEOUT_RECV)

    def destroy(self):
        logger.debug("回收心跳线程...")
        if hasattr(self, "tid_heart"):
            self.tid_heart.join()
        logger.debug("等待关闭socket")
        self.sock.close()

    def stop(self):
        self.isRunning.clear()
        self.protocal.stop()

    def set_protocal(self, protocal):
        """ 载入数据处理器 """
        self.protocal = protocal

        def send_msg(msg, addr=None):
            if addr:
                self.sock.sendto(msg, addr)
            else:
                self.sock.send(msg)

        self.protocal.set_func_send(send_msg)

    def heart_beat(self):
        """ 利用定时器发送心跳 """
        from time import sleep

        def send_heart():
            while self.isRunning.is_set():
                self.sock.send(PACK_HEAD_HEART)
                sleep(HEART_BEAT_PERIOD)

        self.tid_heart = Thread(target=send_heart)
        # self.tid_heart.setDaemon(True)
        self.tid_heart.start()

    def listen(self):
        self.isRunning = Event()
        self.isRunning.set()

        self.timeout_shutdown = 0
        self.heart_beat()

        while self.isRunning.is_set():
            self.recv_msg()
            # 心跳超时
            if self.timeout_shutdown >= TIMEOUT_SHUTDOWN:
                self.stop()

    def recv_msg(self):
        try:
            data, addr = self.sock.recvfrom(BUFFER_SIZE)  # 阻塞，但设置了超时

        except socket.timeout:
            self.timeout_shutdown += TIMEOUT_RECV
            # logger.debug(f"超时计数【{self.timeout_shutdown}】")

        else:
            # 区分指令、心跳、数据
            if data[:4] == PACK_HEAD_HEART:
                # logger.debug("接收到心跳返回")
                self.timeout_shutdown = 0
            elif data[:4] == PACK_HEAD_ERROR:
                reason = data[4:].decode()
                logger.error(f"Except:【{reason}】")
            else:
                self.protocal.parse_reply(data, addr)

    def run(self):
        self.listen()
        # 当监听结束时，也不再允许发送数据
        self.destroy()


if __name__ == "__main__":
    from time import sleep
    from protocal import DpmDataTrans

    client = UdpClient(("127.0.0.1", BROKER_PORT))
    protocal = DpmDataTrans()
    client.set_protocal(protocal)
    client.start()

    protocal.register_assist("123456")
    # client.setDaemon(True)

    try:
        while True:
            data = input("Input the Data ([Q] for quit): ")
            if data == "Q":
                client.stop()  # 关闭监听
                break
            else:
                protocal.send_data(data.encode())
    except OSError:
        logger.info("由于心跳超时，已自动断开连接")

    client.stop()
    client.join()
    client.destroy()
